﻿using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using UFIDA.U9.WSS.ProxyService.Exceptions;
using UFSoft.UBF.Util.Log;

namespace UFIDA.U9.WSS.ProxyService.Behavior.WebHttp.Base
{
    public abstract class NewtonsoftJsonBehavior : WebHttpBehavior
    {
        protected static readonly ILogger Logger = LoggerManager.GetLogger("NewtonsoftJsonBehavior");

        public override void Validate(ServiceEndpoint endpoint)
        {
            base.Validate(endpoint);
            BindingElementCollection elements = endpoint.Binding.CreateBindingElements();
            WebMessageEncodingBindingElement webEncoder = elements.Find<WebMessageEncodingBindingElement>();
            if (webEncoder == null)
            {
                throw new WSException(ExceptionCodes.ServiceRequestBodyFormatException,
                    "This behavior must be used in an endpoint with the WebHttpBinding (or a custom binding with the WebMessageEncodingBindingElement).");
            }

            foreach (var operation in endpoint.Contract.Operations)
            {
                ValidateOperation(operation);
            }
        }

        protected override void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            //clear default erro handlers.
            endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();
            //add our own error handler.
            endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ExtendErrorHandler(endpoint,
                endpointDispatcher));
        }

        protected override IDispatchMessageFormatter GetRequestDispatchFormatter(
            OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            if (IsGetOperation(operationDescription))
            {
                // no change for GET operations
                return base.GetRequestDispatchFormatter(operationDescription, endpoint);
            }

            if (operationDescription.Messages[0].Body.Parts.Count == 0)
            {
                // nothing in the body, still use the default
                return base.GetRequestDispatchFormatter(operationDescription, endpoint);
            }

            return GetCustomDispatchFormatter(operationDescription);
        }

        protected override IDispatchMessageFormatter GetReplyDispatchFormatter(
            OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            if (operationDescription.Messages.Count == 1 ||
                operationDescription.Messages[1].Body.ReturnValue.Type == typeof(void))
            {
                return base.GetReplyDispatchFormatter(operationDescription, endpoint);
            }

            return GetCustomDispatchFormatter(operationDescription);
        }

        /// <summary>
        ///     获取自定义的 DispatchFormatter
        /// </summary>
        /// <param name="operationDescription"></param>
        /// <returns></returns>
        protected abstract IDispatchMessageFormatter GetCustomDispatchFormatter(
            OperationDescription operationDescription);

        private void ValidateOperation(OperationDescription operation)
        {
            if (operation.Messages.Count > 1)
            {
                if (operation.Messages[1].Body.Parts.Count > 0)
                {
                    throw new WSException(ExceptionCodes.ServiceRequestBodyFormatException,
                        "Operations cannot have out/ref parameters.");
                }
            }

            WebMessageBodyStyle bodyStyle = GetBodyStyle(operation);
            int inputParameterCount = operation.Messages[0].Body.Parts.Count;
            if (!IsGetOperation(operation))
            {
                var wrappedRequest = bodyStyle == WebMessageBodyStyle.Wrapped ||
                                     bodyStyle == WebMessageBodyStyle.WrappedRequest;
                if (inputParameterCount == 1 && wrappedRequest)
                {
                    throw new WSException(ExceptionCodes.ServiceRequestBodyFormatException,
                        "Wrapped body style for single parameters not implemented in this behavior.");
                }
            }

            bool wrappedResponse = bodyStyle == WebMessageBodyStyle.Wrapped ||
                                   bodyStyle == WebMessageBodyStyle.WrappedResponse;
            bool isVoidReturn = operation.Messages.Count == 1 ||
                                operation.Messages[1].Body.ReturnValue.Type == typeof(void);
            if (!isVoidReturn && wrappedResponse)
            {
                throw new WSException(ExceptionCodes.ServiceRequestBodyFormatException,
                    "Wrapped response not implemented in this behavior.");
            }
        }

        private WebMessageBodyStyle GetBodyStyle(OperationDescription operation)
        {
            WebGetAttribute wga = operation.Behaviors.Find<WebGetAttribute>();
            if (wga != null)
            {
                return wga.BodyStyle;
            }

            WebInvokeAttribute wia = operation.Behaviors.Find<WebInvokeAttribute>();
            if (wia != null)
            {
                return wia.BodyStyle;
            }

            return DefaultBodyStyle;
        }

        private bool IsGetOperation(OperationDescription operation)
        {
            WebGetAttribute wga = operation.Behaviors.Find<WebGetAttribute>();
            if (wga != null)
            {
                return true;
            }

            WebInvokeAttribute wia = operation.Behaviors.Find<WebInvokeAttribute>();
            if (wia != null)
            {
                return wia.Method == "HEAD";
            }

            return false;
        }
    }
}